#include "server.h"
#include "../message/result_msg.h"

int init_network(network_server * server)
{
	struct event_base *base;
	struct evconnlistener *listener;
	struct event *signal_event;
	struct sockaddr_in sin;
	base = event_base_new();
	server->base = base;
	if (!base) {
		dbg_error("Could not initialize libevent!");
		return 1;
	}

	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(server->listener_port);

	listener = evconnlistener_new_bind(base, listener_cb, (void *)server,
	    LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
	    (struct sockaddr*)&sin,
	    sizeof(sin));

	if (!listener) {
		dbg_error("Could not create a listener!");
		return 1;
	}
	signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)server);

	if (!signal_event || event_add(signal_event, NULL)<0) {
		dbg_error("Could not create/add a signal event!");
		return 1;
	}

	event_base_dispatch(base);

	evconnlistener_free(listener);
	event_free(signal_event);
	event_base_free(base);

	dbg_info("done");
	return 0;

}

void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
    struct sockaddr *sa, int socklen, void *user_data)
{
	dbg_info ("listener_cb------");
	network_server * server = (network_server*)user_data;
	struct event_base *base = server->base;
	struct bufferevent *bev;
	struct bufferevent_entry *elm = (struct bufferevent_entry *) robot_malloc(sizeof(struct bufferevent_entry),true);
	bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
	elm->element.e = bev;
	if (!bev) {
		dbg_error("Error constructing bufferevent!");
		event_base_loopbreak(base);
		return;
	}
	bufferevent_setcb(bev, conn_readcb, conn_writecb, conn_eventcb, user_data);
	bufferevent_enable(bev, EV_WRITE);
	bufferevent_enable(bev, EV_READ);
	//LIST_INSERT_HEAD(server->clientlist,elm,next);
}

void conn_readcb(struct bufferevent *bev, void *user_data)
{
	struct evbuffer *inbuffer = bufferevent_get_input(bev);
	int len = evbuffer_get_length(inbuffer);

	dbg_info ("conn_readcb------");
	if (len == 0) {
		dbg_error("conn_readcb: there is no data in thre buffer");
		//bufferevent_free(bev);
	}
	char readbuff[len] ;
	evbuffer_remove(inbuffer,readbuff,len);
	dbg_info("read data:%.*s",len,readbuff);
	printfmsg(readbuff,len);
	process_data(readbuff,len,bev,user_data);

}

void conn_writecb(struct bufferevent *bev, void *user_data)
{
	dbg_info ("conn_writecb------");
	struct evbuffer *output = bufferevent_get_output(bev);
	if (evbuffer_get_length(output) == 0) {
		dbg_info("flushed answer\n");
		//bufferevent_free(bev);
	}
}

void conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
	if (events & BEV_EVENT_EOF) {
		dbg_info("Connection closed.\n");
	} else if (events & BEV_EVENT_ERROR) {
		dbg_error("Got an error on the connection: %s\n",strerror(errno));/*XXX win32*/
	}
	/* None of the other events can happen here, since we haven't enabled
	 * timeouts */
	bufferevent_free(bev);
}

void signal_cb(evutil_socket_t sig, short events, void *user_data)
{
	network_server * server = user_data;
	struct event_base *base = server->base;
	struct timeval delay = { 2, 0 };

	dbg_error("Caught an interrupt signal; exiting cleanly in two seconds.\n");

	event_base_loopexit(base, &delay);
}




//ÒµÎñÂß¼­´¦Àí
int process_data(char* data,int len,struct bufferevent* bev,void * userdata)
{
	int ret = 0;
	int read_cursor = 0;
	dbg_info ("process_data------");
	while(read_cursor < len){
		char msg_type = GET_MSG_TYPE(data+read_cursor);
		switch(msg_type)
		{	
			case MSG_TYPE_CTL_CAR : {
				ctl_car_msg * msg = NULL;
				ret = deserialize_ctlcar_msg(data+read_cursor,len-read_cursor,&msg);
				if(!ret && msg)
				{
					char * ret_msg_ = NULL;
					int ret_msg_len = 0;
					ret = process_ctlcar_msg(msg,userdata);
					dbg_info("process car msg result:%d",ret);
					//send result
					result_msg * ret_msg = create_result_msg(msg->user_id,msg->cmd,ret);
					ret = serialize_result_msg(ret_msg,&ret_msg_,&ret_msg_len);
					bufferevent_write(bev,ret_msg_,ret_msg_len);
					read_cursor +=  msg->msg_->len + MSG_HEAD_LEN;
					
					//free msg buffer
					if(ret_msg)
						distory_result_msg(ret_msg);
					if(ret_msg_)
						free(ret_msg_);
					distory_ctl_car_msg(msg);
				}else{
					dbg_error("error msg:%.*s",len-read_cursor,data+read_cursor);
					read_cursor = len;//break while
				}
			} break;
			case MSG_TYPE_CTL_CAR_HAND : {
				ctl_hand_msg * msg = NULL;
				ret = deserialize_ctl_hand_msg(data+read_cursor,len-read_cursor,&msg);
				if(!ret && msg)
				{
					char * ret_msg_ = NULL;
					int ret_msg_len = 0;
					ret = process_ctlhand_msg(msg,userdata);
					dbg_info("process car msg result:%d",ret);
					//send result
					result_msg * ret_msg = create_result_msg(msg->user_id,msg->cmd,ret);
					ret = serialize_result_msg(ret_msg,&ret_msg_,&ret_msg_len);
					bufferevent_write(bev,ret_msg_,ret_msg_len);
					read_cursor +=  msg->msg_->len + MSG_HEAD_LEN;
					
					//free msg buffer
					if(ret_msg)
						distory_result_msg(ret_msg);
					if(ret_msg_)
						free(ret_msg_);
					distory_ctl_hand_msg(msg);
				}else{
					dbg_error("error msg:%.*s",len-read_cursor,data+read_cursor);
					read_cursor = len;//break while
				}
			} break;
			case MSG_TYPE_USER_CMD : {
				dbg_error("process use cmd and ignore it,msgtype:%d, cursor:%d,len:%d",msg_type,read_cursor,len);
				read_cursor = len;//break while
			} break;

			default:{
				dbg_error("process data for unkown msgtype:%d,cursor:%d,len:%d",msg_type,read_cursor,len);
				read_cursor = len;//break while
				break;
			}
		}
		
	}
	return ret;
}



int process_ctlhand_msg(ctl_hand_msg* msg,void * userdata)
{
	network_server * server = userdata;
	//application * app = server->app;
	int ret = 0;
	dbg_info ("process_ctlhand_msg----user_id:%d,cmd:%d,motion_size:%d--",msg->user_id,msg->cmd,msg->motion_size);

	switch(msg->cmd)
	{									
		case CTL_CAR_CMD_CTL_MOTION			:{ 
			ret = car_ctl_motions(msg->motion_datas_,msg->motion_size);
			dbg_info ("car_ctl_motions ret:%d",ret);
		}break;
		case CTL_CAR_CMD_CTL_MOTION_GROUP	:{ 
			ret = car_ctl_motion_group(msg->group_data_);
			dbg_info ("car_ctl_motion_group ret:%d",ret);
		}break;
		default :
			dbg_info("error car cmd:%d",msg->cmd);
	}
	return 0;
}


int process_ctlcar_msg(ctl_car_msg* msg,void * userdata)
{
	network_server * server = userdata;
	//application * app = server->app;
    dbg_info ("process_carmsg----user_id:%d,cmd:%d,speed:%d",msg->user_id,msg->cmd,msg->speed);

    switch(msg->cmd)
    {
        case CTL_CAR_CMD_MOVE_UP:{
            t_up(msg->speed,0);
        }break;
        case CTL_CAR_CMD_MOVE_DOWN:{
            t_down(msg->speed,0);
        }break;
        case CTL_CAR_CMD_MOVE_LEFT:{
            move_left(msg->speed,0);
        }break;
        case CTL_CAR_CMD_MOVE_RIGHT:{
            move_right(msg->speed,0);
        }break;
        case CTL_CAR_CMD_TURN_LEFT:{
            turn_left(msg->speed,0);
        }break;
        case CTL_CAR_CMD_TURN_RIGHT:{
            turn_right(msg->speed,0);
        }break;
        case CTL_CAR_CMD_FORWARD_LEFT:{
            forward_left(msg->speed,0);
        }break;
        case CTL_CAR_CMD_FORWARD_RIGHT:{
            forward_right(msg->speed,0);
        }break;
        case CTL_CAR_CMD_BACKWARD_LEFT:{
            backward_left(msg->speed,0);
        }break;
        case CTL_CAR_CMD_BACKWARD_RIGHT:{
            backward_right(msg->speed,0);
        }break;
        case CTL_CAR_CMD_STOP :{
             t_stop(0);
        }break;
        default :{
            dbg_info("error car cmd:%d",msg->cmd);
        }
    }

	return 0;
}
